#include <iostream>
#include <cstring>
#include <cstdio>
#include <signal.h>
#include <unistd.h>

using namespace std;

void Usage()
{
    printf("\nUsage : ./mysignal pid signo\n");
}

// int cnt = 0;
long long cnt = 0;
void catchSig(int signo)
{
    // std::cout << "我是一个进程, 我的pid是: " << getpid() << " 信号编号是: " << signo << std::endl;
    // std::cout << "1s之后cnt : " << cnt << " 收到的信号编号 : " << signo << endl;

    std::cout << " 收到的信号编号 : " << signo << endl;

    // exit(0);
    
    // 再设闹钟，每次收到14号信号都会进入到这个函数里面
    // alarm(1);
}


// ./mysignal pid signo
// int main(int argc, char *argv[])
int main()
{
    // 最后一个测试，如果我对所以信号都做了自定义动作呢？
    for(int i = 1; i <= 31; i++)
    {
        signal(i, catchSig);
    }

    // 即使1-31的普通信号都做了自定义动作，OS也至少保留了一个管理员信号来杀掉所有异常进程

    while(true)
    {
        cout << "我是一个进程, 我的pid是 : " << getpid() << endl;
        sleep(1);
    }


    // 1. 通过键盘发送信号
    // while(true)
    // {
    //     std::cout << "hello world" << std::endl;
    //     sleep(1);
    // }


    // 2.通过系统调用产生信号
    /*
    if(argc != 3)
    {
        Usage();
        exit(1);
    }

    pid_t pid = atoi(argv[1]);
    int signo = atoi(argv[2]);

    // kill()可以向任意进程发送信号
    int n = kill(pid, signo);
    if(n != 0)
    {
        perror("kill");
    }
    */

    // raise():向自己发送任意信号               -- kill(getpid(), signo)
    // abort()给自己发送指定信号-6号SIGABRT     -- kill(getpid(), SIGABRT)
    /*
    int cnt = 0;

    while(true)
    {
        cout << "cnt : " << cnt << endl;

        if(cnt++ == 5)
        {
            // int signo;
            // cin >> signo;
            // raise(signo);

            // abort();

            kill(getpid(), SIGABRT);
        }
        sleep(1);
    } 
    */

    // 很多的情况，进程收到的大部分信号，默认处理动作都是终止进程
    // 信号的意义：都是终止进程，为什么要有那么多信号？
    // 信号的不同，代表不同的事件，但是对于不同的事件处理动作可以一样！不同信号 --- 不同终止原因


    // 3.硬件异常产生信号
    // 信号产生，不一定用户显式发送

    /*
    signal(SIGFPE, catchSig);

    int a = 10;
    a /= 0;

    // 除零错误，会产生一个无穷大的数 --> 溢出 --> 状态寄存器会有溢出标志位被置为1，cpu会出异常，OS识别到了

    // 当进程被切换的时候，就有无数次状态寄存器被保存和回复的过程
    // 所以每一次恢复的时候，就让OS识别到了CPU内部的状态寄存器中的溢出标志位是1
    // OS如何得知应该给当前进程发送8号信号的-- OS怎么知道我除0了呢？？，CPU会异常
    
    while (true)
    {
        std::cout << "我在运行中...." << std::endl;
        sleep(1);

        int *p = nullptr;
        // p = nullptr; // 这句代码是对的
        // OS怎么知道呢？？我野指针了呢？
        *p = 100;  //为什么 野指针 就会崩溃呢？因为OS会给当前进程发送指定的11号信号
    }

    // 通过上面两个例子：都是由于硬件出现异常(一个是CPU的状态寄存器，一个是CPU的内存管理单元MMU)
    // 这个异常被OS知道了，所以向导致这种异常的进程发送相应信号

    */

    // 4. 软件条件产生信号 
    // "闹钟"其实就是用软件实现的

    // int i = 0;
    // while(i != 5)
    // {
    //     cout << "alarm()之前" << endl;
    //     sleep(1);
    //     i++;
    // }

    // int cnt = 0;

    // alarm(1);   // 执行到这句代码，进程就设了一个1s钟之后终止的闹钟
    // // 1s之后操作系统就会给我这个进程发送14号信号SIGALRM

    // while(true)
    // {
    //     cout << "cnt : " << cnt++ << endl;
    // }

    // 上面的IO其实很慢(准确来说不仅有磁盘IO，还有云服务器传输数据的网络IO)
    // 所以无法准确知道CPU在1S左右，我们的计算机能够将数据累计多少次!
    // 下面可以通过alarm(1)大概的测算出来 -- 5亿多
    // signal(SIGALRM, catchSig);
    // alarm(1);
    // while(true)
    // {
    //     cnt++;
    // }

    

    // 核心转储：进程出现异常时，如果发送某些带有core的信号时，OS在对应的时刻，会将该进程在内存中的有效数据转储到磁盘中！
    // 为什么要有？ --> 支持调试
    // gdb core-file 命令  core.pid  就可以查看出错的位置等相关信息
    int a[10];
    // a[100] = 100;     
    // 上面这句代码已经越界了。
    // 然而，C语言和操作系统并不会在你越界时立即抛出错误。错误只有在你尝试访问某个操作系统已经确定不属于你的进程的内存区域时才会发生。
    // 在现代操作系统中，内存访问由内存管理单元（MMU）和操作系统的内存保护机制来控制。
    // 如果你的越界访问恰好落在了进程的地址空间内，而且没有触及到一个受保护的内存段（如未分配的页面），那么可能不会立即产生错误。
    // 当你的访问试图读取或写入一个不属于你的进程地址空间的内存区域时（例如，操作系统确定该地址未被映射或权限不足），操作系统就会介入，发送SIGSEGV信号给进程，这通常会导致程序崩溃。

    // a[10000] = 100;     // Segmentation fault -- 11号信号



    return 0;
}